#include "../log_local/logger.h"
#include <vector>
#include <thread>
#include <chrono>
using namespace ns_log;
using namespace ns_helper;

void bench(const std::string &logger_name, int thread_num, size_t log_num, size_t per_size)
{
    // 1. 获取全局日志器
    Logger::ptr logger = getLogger(logger_name);
    // 2. 构造日志body
    std::string body = std::string(per_size - 1, 'A'); // 为'\n'流出位置
    // 3. 创建线程，并为其分配要打印的日志数量
    size_t per_count = log_num / thread_num;
    std::vector<std::thread> thread_vec(thread_num);
    // 4. 维护每个线程运行的时间
    std::vector<double> time_vec(thread_num);
    // 5. 启动线程，打印日志
    for (int i = 0; i < thread_num; i++)
    {
        // i必须要传值捕捉，因为主线程会修改i, 所以i线程不安全
        thread_vec[i] = std::thread(
            [&, i, per_count]() mutable
            {
                // 1. 补充日志数量
                if (i == thread_num - 1)
                    per_count += log_num % thread_num;
                // 2. 计时
                auto start = std::chrono::high_resolution_clock::now();
                for (int j = 0; j < per_count; j++)
                {
                    logger->fatal("%s", body.c_str());
                }
                // 3. 取消计时
                auto end = std::chrono::high_resolution_clock::now();
                // 4. 记录时间
                std::chrono::duration<double> tm = end - start;
                time_vec[i] = tm.count();
                // 5. 打印运行状况
                std::cout << "\t线程-" << i << "\t打印日志数：" << per_count << "\t总耗时：" << time_vec[i] << "秒\n";
            });
    }
    // 6. join线程
    for (auto &worker : thread_vec)
    {
        worker.join();
    }
    // 7. 遍历求出最长时间
    // 每个线程之间都是并发运行的，因此耗时最长的线程就是日志打印总耗时
    double max_time = 0.0;
    for (auto &elem : time_vec)
    {
        max_time = std::max(max_time, elem);
    }
    // 8. 打印总结果
    // 总耗时、每秒输出日志数量、每秒输出日志大小
    double per_second_num = (1.0 * log_num) / max_time;
    double per_second_size = (log_num * per_size * 1.0) / (max_time * 1024); // 以KB为单位
    std::cout << "\t总耗时：" << max_time << "\n";
    std::cout << "\t每秒输出日志数量：" << per_second_num << "\n";
    std::cout << "\t每秒输出日志大小：" << per_second_size << "\n";
}

const std::string sync_logger_name = "sync_logger";
const std::string async_safe_logger_name = "async_safe_logger";
const std::string async_unsafe_logger_name = "async_unsafe_logger";

const std::string sync_logger_db = "./logdb/sync.db";
const std::string async_safe_logger_db = "./logdb/async_safe.db";
const std::string async_unsafe_logger_db = "./logdb/async_unsafe.db";

const std::string mysql_conf = "./mysql.conf";

void init_sqlite()
{
    // 创建全局日志器
    LoggerBuilder::ptr builder = std::make_shared<GlobalLoggerBuilder>();
    // 1. 同步日志器
    builder->buildLoggerName(sync_logger_name);
    builder->buildLoggerType(LoggerType::SYNC);
    builder->buildDBLogSink<SqliteSink>(sync_logger_db);
    builder->build();
    // 2. 异步安全日志器
    builder->buildLoggerName(async_safe_logger_name);
    builder->buildLoggerType(LoggerType::ASYNC);
    builder->buildDBLogSink<SqliteSink>(async_safe_logger_db);
    builder->buildAsyncType(AsyncType::ASYNC_SAFE);
    builder->build();
    // 3. 异步不安全日志器
    builder->buildLoggerName(async_unsafe_logger_name);
    builder->buildLoggerType(LoggerType::ASYNC);
    builder->buildDBLogSink<SqliteSink>(async_unsafe_logger_db);
    builder->buildAsyncType(AsyncType::ASYNC_UNSAFE);
    builder->build();
}

void init_mysql()
{
    // 创建全局日志器
    LoggerBuilder::ptr builder = std::make_shared<GlobalLoggerBuilder>();
    // 1. 同步日志器
    builder->buildLoggerName(sync_logger_name);
    builder->buildLoggerType(LoggerType::SYNC);
    builder->buildDBLogSink<MySQLSink>(mysql_conf);
    builder->build();
    // 2. 异步安全日志器
    builder->buildLoggerName(async_safe_logger_name);
    builder->buildLoggerType(LoggerType::ASYNC);
    builder->buildDBLogSink<MySQLSink>(mysql_conf);
    builder->buildAsyncType(AsyncType::ASYNC_SAFE);
    builder->build();
    // 3. 异步不安全日志器
    builder->buildLoggerName(async_unsafe_logger_name);
    builder->buildLoggerType(LoggerType::ASYNC);
    builder->buildDBLogSink<MySQLSink>(mysql_conf);
    builder->buildAsyncType(AsyncType::ASYNC_UNSAFE);
    builder->build();
}

void sync_test(int thread_num, size_t log_num, size_t per_size)
{
    bench(sync_logger_name,  thread_num, log_num, per_size);
}

void async_safe_test(int thread_num, size_t log_num, size_t per_size)
{
    bench(async_safe_logger_name,  thread_num, log_num, per_size);
}

void async_unsafe_test(int thread_num, size_t log_num, size_t per_size)
{
    bench(async_unsafe_logger_name, thread_num, log_num, per_size);
}

const int log_num = 1024 * 1024;
const int per_size = 100;

// ./bench sqlite3:1 mysql:2 sync:1 async_safe:2 async_unsafe:3 thread_num
int main(int argc, char *argv[])
{
    if (argc != 4)
    {
        std::cout << "Usage: " << "./bench sqlite3:1 mysql:2 sync:1 async_safe:2 async_unsafe:3 " << " thread_num\n";
        return 1;
    }

    int thread_num = std::stoi(argv[3]);
    bool is_sqlite3 = std::stoi(argv[1]) == 1;
    if (is_sqlite3)
    {
        init_sqlite();
    }
    else
    {
        init_mysql();
    }
    switch (std::stoi(argv[2]))
    {
        case (1):
        {
            sync_test(thread_num, log_num, per_size);
            break;
        }
        case (2):
        {
            async_safe_test(thread_num, log_num, per_size);
            break;
        }
        case (3):
        {
            async_unsafe_test(thread_num, log_num, per_size);
            break;
        }
    }

    return 0;
}
